package com.mugui.spring.net.task;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.mugui.Mugui;
import com.mugui.spring.base.Task;
import com.mugui.spring.base.TaskInterface;
import com.mugui.spring.net.sys.SysLogManager;
import com.mugui.spring.util.RedisAccess;
import com.mugui.util.Other;

public class TaskManager implements Mugui {
	private static TaskManager taskManager = null;

	public static final TaskManager getTaskManager() {
		if (taskManager == null) {
			taskManager = new TaskManager();

		}
		return taskManager;
	}

	private boolean isrun = true;

	public void stop() {
		isrun = false;
	}

	public void init() {
		if (outTimeThread == null || !outTimeThread.isAlive()) {
			isrun = true;
			outTimeThread = new TaskRunThread();
			outTimeThread.setName("outTimeThread");
			outTimeThread.start();
		}

	}

	private class TempBean {
		private long out_time;
		private TaskInterface object;
		private Task task;

	}

	private static TaskRunThread outTimeThread = null;

	private class TaskRunThread extends Thread {
		/**
		 * 一个根据时间进行维护的队列
		 */
		private class TaskQueue {
			TempBean[] queue = new TempBean[128];
			int size = 0;

			int size() {
				return size;
			}

			private final byte REMOVE = 1;
			private final byte FIXUP = 2;
			private final byte FIXDOWN = 3;

			TempBean remove() {
				return handle(1, REMOVE);
			}

			TempBean remove(int index) {
				return handle(index, REMOVE);
			}

			TempBean remove0(int index) {
				TempBean bean = queue[index];
				for (int i = index; i < size; i++) {
					queue[i] = queue[i + 1];
				}
				queue[size] = null;
				size--;
				return bean;
			}

			private TempBean handle(int index, byte type) {
				synchronized (queue) {
					switch (type) {
					case REMOVE:
						return remove0(index);
					case FIXUP:
						return fixUp0(index);
					case FIXDOWN:
						return fixDown0(index);
					default:
						return null;
					}
				}

			}

			void add(TempBean bean) {
				for (int i = 1; i < size; i++) {
					if (queue[i] != null && queue[i].object == bean.object) {
						throw new RuntimeException("反复运行的相同任务" + bean.object);
					}
				}
				size++;
				if (queue.length == size) {
					queue = Arrays.copyOf(queue, 2 * size);
				}
				queue[size] = bean;
				fixUp(size);
			}

			/**
			 * 向前更新时间
			 */
			private TempBean fixUp0(int index) {
				for (int i = index; i > 1; i--) {
					if (queue[i] == null) {
						remove0(i);
						continue;
					}
					if (queue[i - 1] == null) {
						throw new NullPointerException("i - 1=" + (i - 1) + " index=" + index);
					}
					if (queue[i].out_time < queue[i - 1].out_time) {
						queue[0] = queue[i];
						queue[i] = queue[i - 1];
						queue[i - 1] = queue[0];
					} else {
						break;
					}
				}
				return null;
			}

			private void fixUp(int index) {
				handle(index, FIXUP);
			}

			/**
			 * 向后更新
			 * 
			 * @param out_time_index
			 */
			public TempBean fixDown0(int index) {
				for (int i = index; i < size; i++) {
					if (queue[i].out_time > queue[i + 1].out_time) {
						queue[0] = queue[i];
						queue[i] = queue[i + 1];
						queue[i + 1] = queue[0];
					} else {
						break;
					}
				}
				return null;
			}

			public void fixDown(int index) {
				handle(index, FIXDOWN);
			}

			public TempBean poll() {
				return queue[1];
			}

			public void remove(TempBean bean) {
				for (int i = 0; i < size; i++) {
					if (queue[i] != null && queue[i].object == bean.object) {
						remove(i);
						return;
					}
				}
			}

		}

		private TaskQueue outTimeQueue = new TaskQueue();

		@Override
		public void run() {
			while (isrun) {
				try {
					TempBean bean = null;
					synchronized (outTimeQueue) {
						while (outTimeQueue.size() == 0) {
							outTimeQueue.wait(15000);
						}
						bean = outTimeQueue.poll();
						if (bean == null) {
							continue;
						}
						long wait_time = bean.out_time - System.currentTimeMillis();
						if (wait_time > 0) {
							outTimeQueue.wait(wait_time);
							continue;
						}
						bean = outTimeQueue.remove();
					}
					if (bean != null) {
						try {
							new TaskThread(bean.object);
						} catch (Exception e) {
							e.printStackTrace();
						}
						if (bean.task.value() == Task.CYCLE) {
							int task_time = bean.task.blank() == 0 ? 1000 : bean.task.blank();
							bean.out_time = System.currentTimeMillis() + task_time;
							addMapBodyBean(bean);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		public void addMapBodyBean(TempBean mapBodyBean) {
			synchronized (outTimeQueue) {
				outTimeQueue.add(mapBodyBean);
				outTimeQueue.notifyAll();
			}
		}

		public void removeMapBodyBean(TempBean bean) {
			synchronized (outTimeQueue) {
				outTimeQueue.remove(bean);
				outTimeQueue.notifyAll();
			}
		}
	}

	private static final HashMap<Class<? extends TaskInterface>, TaskThread> TaskMap = new HashMap<>();

	public class TaskThread {
		private TempThread th = null;

		public TaskThread(TaskInterface object) {
			TaskThread thread = null;
			if ((thread = TaskMap.get(object.getClass())) == null) {
				TaskMap.put(object.getClass(), thread = this);
			}
			if (thread.th == null || !thread.th.isAlive()) {
				thread.th = new TempThread();
				thread.th.setName(object.getClass().getName());
				thread.th.start();
			}
			thread.th.add(object);
		}

	}

	private class TempThread extends Thread {

		private ConcurrentLinkedQueue<TaskInterface> list = new ConcurrentLinkedQueue<>();

		public void add(TaskInterface object) {
			list.add(object);
		}

		@Override
		public void run() {
			while (true) {
				TaskInterface taskInterface = null;
				boolean listener_sys_info = false;
				boolean listener_sys_error = false;
				try {
					while ((taskInterface = list.poll()) == null) {
						Other.sleep(500);
					}
					if ("true".equals(redisAccess.get("listener_sys_info"))) {
						listener_sys_info = true;
					} else {
						listener_sys_info = false;
					}
					if ("true".equals(redisAccess.get("listener_sys_error"))) {
						listener_sys_error = true;
					} else {
						listener_sys_error = false;
					}
					try {
						if (listener_sys_info) {
							sysLogManager.TaskLog().info(appliction_name, taskInterface.getClass().getName() + " 运行开始");
						}
					} catch (Exception e) {
						// TODO: handle exception
					}

					taskInterface.run();
					try {
						if (listener_sys_info) {
							sysLogManager.TaskLog().info(appliction_name, taskInterface.getClass().getName() + " 运行结束");
						}
					} catch (Exception e) {
						// TODO: handle exception
					}
				} catch (Exception e) {
					e.printStackTrace();
					try {
						if (listener_sys_error) {
							sysLogManager.TaskLog().error(taskInterface.getClass().getName(), e);
						}
					} catch (Exception e2) {
						// TODO: handle exception
					}
				}
			}
		}
	}

	private static HashMap<Class<? extends TaskInterface>, Field[]> hashMap = new HashMap<>();

	private ApplicationContext applicationContext = null;

	private SysLogManager sysLogManager = null;
	public RedisAccess redisAccess = null;
	private String appliction_name = "未命名";

	public void add(TaskInterface object) {
		if (applicationContext == null) {
			applicationContext = (ApplicationContext) System.getProperties().get("Application");
		}
		if (applicationContext != null && sysLogManager == null) {
			redisAccess = applicationContext.getBean(RedisAccess.class);
			sysLogManager = applicationContext.getBean(SysLogManager.class);
			String property = applicationContext.getEnvironment().getProperty("spring.application.name");
			if (StringUtils.isNotBlank(property)) {
				appliction_name = property;
			}
		}
		if (object.getClass().isAnnotationPresent(Task.class)) {
			Task task = object.getClass().getAnnotation(Task.class);

			Field[] fields = new Field[0];
			try {
				if ((fields = hashMap.get(object.getClass())) == null) {
					if (object.getClass().isAnnotationPresent(Component.class)) {
						Field[] temp = object.getClass().getDeclaredFields();
						ArrayList<Field> list = new ArrayList<>();
						boolean bool = false;
						try {
							Class.forName("org.apache.dubbo.config.annotation.Reference");
							Class.forName("com.alibaba.dubbo.config.annotation.Reference");
							bool = true;
						} catch (Exception e) {
						}

						for (Field field : temp) {
							if (field.isAnnotationPresent(Autowired.class)) {
								list.add(field);
							} else if (bool) {
								if (field.isAnnotationPresent(Reference.class)) {
									list.add(field);
								} else if (field
										.isAnnotationPresent(com.alibaba.dubbo.config.annotation.Reference.class)) {
									list.add(field);
								}
							}
						}
						fields = list.toArray(new Field[0]);
					}
				}
				if (fields != null)
					for (Field field : fields) {
						field.setAccessible(true);
						field.set(object, field.get(applicationContext.getBean(object.getClass())));
					}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				hashMap.put(object.getClass(), fields);
			}

			TempBean bean = new TempBean();
			bean.task = task;
			bean.object = object;
			int task_time = task.time() == 0 ? 1000 : task.time();
			bean.out_time = System.currentTimeMillis() + task_time;
			outTimeThread.addMapBodyBean(bean);
			return;
		}
		throw new NullPointerException("未添加注解@Task");
	}

	@Deprecated
	public void remove(TaskInterface object) {
		if (object.getClass().isAnnotationPresent(Task.class)) {
			Task task = object.getClass().getAnnotation(Task.class);
			TempBean bean = new TempBean();
			bean.task = task;
			bean.object = object;
			outTimeThread.removeMapBodyBean(bean);
			return;
		}
		throw new NullPointerException("未添加注解@Task");
	}
}
